package com.kstudio.videocapture.editvideo;

import java.io.IOException;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.kstudio.videocapture.util.KLog;

public class KVideoEditPlayer extends SurfaceView implements SurfaceHolder.Callback, OnCompletionListener, OnErrorListener, OnPreparedListener {
  private final KLog log = new KLog("KVideoEdit");

  private static final int MSG_PLAYING_TIME_UPDATE = 0x0001;

  private MediaPlayer   mMediaPlayer;
  private SurfaceHolder mSurfaceHolder;
  private KVideoEditPlayerListener mListener;

  private String mVideoPath;
  private int mDuration;
  private int mSeekWhenPrepared;
  private int mSeekBarDelay;

  private int mWidth;
  private int mHeight;

  private KVideoState mKVideoState;

  private enum KVideoState {
      STATE_IDLE,
      STATE_PREPARING,
      STATE_PREPARED,
      STATE_PLAYING,
      STATE_PAUSED,
      STATE_ERROR
  }

  private final Handler mHandler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
          switch (msg.what) {
              case MSG_PLAYING_TIME_UPDATE:
                  log.i("KVideoEditPlayer : MSG_PLAYING_TIME_UPDATE");
                  if(IsPlaying()) {
                      if(mListener != null) {
                          mListener.onKVideoEditPlayerSeekBarUpdate(mMediaPlayer.getCurrentPosition());
                          mHandler.sendEmptyMessageDelayed(MSG_PLAYING_TIME_UPDATE, mSeekBarDelay);
                      }
                  }
                  break;

              default:
                  break;
          }
      }
  };

  public interface KVideoEditPlayerListener {
      void onKVideoEditPlayerPrepared();
      void onKVideoEditPlayerError();
      void onKVideoEditPlayerCompletion();
      void onKVideoEditPlayerSeekBarSetDuration(int duration);
      int  onKVideoEditPlayerSeekBarGetWidth();
      void onKVideoEditPlayerSeekBarUpdate(int time);
  }

  public KVideoEditPlayer(Context context) {
      this(context, null);
  }

  public KVideoEditPlayer(Context context, AttributeSet attrs) {
      this(context, attrs , 0);
  }

  public KVideoEditPlayer(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
      this.getHolder().addCallback(this);

      mVideoPath = null;
      mSeekWhenPrepared = 0;
      mSeekBarDelay = 1000;
      mMediaPlayer = null;
      mSurfaceHolder = null;
      mKVideoState = KVideoState.STATE_IDLE;
  }

  public void KSetVideoPlayerListener(KVideoEditPlayerListener listener) {
      mListener = listener;
  }

  public void KSetVideoPath(String videoPath) {
      log.i("KVideoEditPlayer : KSetVideoPath");

      mVideoPath = videoPath;
      Open();
  }

  public void KSeekTo(int msec) {
      log.i("KVideoEditPlayer : KseekTo = %d", msec);

      if(IsInPlaybackState()) {
          mMediaPlayer.seekTo(msec);
          mSeekWhenPrepared = -1;
      } else {
          mSeekWhenPrepared = msec;
      }
  }

  public int KGetCurrentTime() {
      if(IsInPlaybackState()) {
          int cTime = mMediaPlayer.getCurrentPosition();
          if(cTime < 0) cTime = 0;
          return cTime;
      }
      log.e("KVideoEditPlayer : KgetCurrentTime not Playing state");
      return 0;
  }

  public boolean KPlay() {
      if(IsPlayPossible()) {
          mMediaPlayer.start();

          int seekbarWidth = (mListener != null ? mListener.onKVideoEditPlayerSeekBarGetWidth() : 0);
          if(seekbarWidth > 0 ) {
              mSeekBarDelay = mDuration / seekbarWidth;
              if(mSeekBarDelay < 100) {
                  mSeekBarDelay = 100;
              }
          } else {
              mSeekBarDelay = 1000;
          }
          mHandler.sendEmptyMessageDelayed(MSG_PLAYING_TIME_UPDATE, mSeekBarDelay);

          mKVideoState = KVideoState.STATE_PLAYING;
          return true;
      }
      return false;
  }

  public boolean KPause() {
      if(IsPlaying()) {
          mMediaPlayer.pause();
          mKVideoState = KVideoState.STATE_PAUSED;
          return true;
      }
      return false;
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      int width = getDefaultSize(mWidth, widthMeasureSpec);
      int height = getDefaultSize(mHeight, heightMeasureSpec);

      if (mWidth > 0 && mHeight > 0) {
          if ( mWidth * height  > width * mHeight ) {
              height = width * mHeight / mWidth;
          } else if ( mWidth * height  < width * mHeight ) {
              width = height * mWidth / mHeight;
          }
      }
      setMeasuredDimension(width, height);
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
      log.i("KVideoEditPlayer : surfaceCreated");

      mSurfaceHolder = holder;
      Open();
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width,
          int height) {
      log.i("KVideoEditPlayer : surfaceChanged");
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
      log.i("KVideoEditPlayer : surfaceDestroyed");

      mSurfaceHolder = null;
      Release();
  }

  static class SavedState extends BaseSavedState {
      int seekTime;

      SavedState(Parcelable superState) {
          super(superState);
      }

      private SavedState(Parcel in) {
          super(in);
          seekTime = in.readInt();
      }

      @Override
      public void writeToParcel(Parcel out, int flags) {
          super.writeToParcel(out, flags);
          out.writeInt(seekTime);
      }

      public static final Parcelable.Creator<SavedState> CREATOR
              = new Parcelable.Creator<SavedState>() {
          public SavedState createFromParcel(Parcel in) {
              return new SavedState(in);
          }

          public SavedState[] newArray(int size) {
              return new SavedState[size];
          }
      };
  }

  @Override
  protected Parcelable onSaveInstanceState() {
      Parcelable superState = super.onSaveInstanceState();
      SavedState ss = new SavedState(superState);

      if(mMediaPlayer != null) {
          ss.seekTime = mMediaPlayer.getCurrentPosition();
      }
      return ss;
  }

  @Override
  protected void onRestoreInstanceState(Parcelable state) {
      SavedState ss = (SavedState) state;
      super.onRestoreInstanceState(ss.getSuperState());

      mSeekWhenPrepared = ss.seekTime;
  }

  @Override
  public boolean onError(MediaPlayer mp, int what, int extra) {
      log.i("KVideoEditPlayer : onError");

      if(mListener != null) mListener.onKVideoEditPlayerError();
      return false;
  }

  @Override
  public void onCompletion(MediaPlayer mp) {
      log.i("KVideoEditPlayer : onCompletion");

      mHandler.removeMessages(MSG_PLAYING_TIME_UPDATE);
      mKVideoState = KVideoState.STATE_PREPARED;
      if(mListener != null) mListener.onKVideoEditPlayerCompletion();
  }

  @Override
  public void onPrepared(MediaPlayer mp) {
      log.i("KVideoEditPlayer : onPrepared");

      mDuration = mp.getDuration();
      SetExternalDuration(mDuration);
      mKVideoState = KVideoState.STATE_PREPARED;

      mWidth = mp.getVideoWidth();
      mHeight = mp.getVideoHeight();

      int seekToPosition = mSeekWhenPrepared;
      if (seekToPosition >= 0) {
          KSeekTo(seekToPosition);
      }

      if (mWidth != 0 && mHeight != 0) {
          getHolder().setFixedSize(mWidth, mHeight);
      }

      VideoEditPlayerPrepared();
  }


  private void Open() {
      if(mVideoPath == null || mSurfaceHolder == null) {
          return;
      }
      log.i("KVideoEditPlayer : Open");

      // Tell the music playback service to pause
      // TODO: these constants need to be published somewhere in the framework.
//      Intent i = new Intent("com.android.music.musicservicecommand");
//      i.putExtra("command", "pause");
//      mContext.sendBroadcast(i);

      Release();
      try {
          mKVideoState = KVideoState.STATE_PREPARING;

          mMediaPlayer = new MediaPlayer();
          mMediaPlayer.setOnPreparedListener(this);
//          mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
          mDuration = -1;
          mMediaPlayer.setOnCompletionListener(this);
          mMediaPlayer.setOnErrorListener(this);
//          mMediaPlayer.setOnInfoListener(mOnInfoListener);
//          mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
//          mCurrentBufferPercentage = 0;
          mMediaPlayer.setDataSource(mVideoPath);
          mMediaPlayer.setDisplay(mSurfaceHolder);
          mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
          mMediaPlayer.setScreenOnWhilePlaying(true);
          mMediaPlayer.prepareAsync();

      } catch (IllegalArgumentException e) {
          if(mListener != null) mListener.onKVideoEditPlayerError();
          e.printStackTrace();
      } catch (SecurityException e) {
          if(mListener != null) mListener.onKVideoEditPlayerError();
          e.printStackTrace();
      } catch (IllegalStateException e) {
          if(mListener != null) mListener.onKVideoEditPlayerError();
          e.printStackTrace();
      } catch (IOException e) {
          if(mListener != null) mListener.onKVideoEditPlayerError();
          e.printStackTrace();
      }
  }

  private void Release() {
      log.i("KVideoEditPlayer : Release");

      if (mMediaPlayer != null)
      {
          mMediaPlayer.reset();
          mMediaPlayer.stop();
          mMediaPlayer.release();
          mMediaPlayer = null;
      }
      mKVideoState = KVideoState.STATE_IDLE;
  }

  private boolean IsInPlaybackState() {
      log.i("KVideoEditPlayer : KisInPlaybackState");

      return (mMediaPlayer != null &&
              mKVideoState != KVideoState.STATE_ERROR &&
              mKVideoState != KVideoState.STATE_IDLE &&
              mKVideoState != KVideoState.STATE_PREPARING);
  }

  private boolean IsPlayPossible() {
      return (mMediaPlayer != null &&
              (mKVideoState == KVideoState.STATE_PREPARED ||
              mKVideoState == KVideoState.STATE_PAUSED));
  }

  private boolean IsPlaying() {
      return (mMediaPlayer != null &&
              mKVideoState == KVideoState.STATE_PLAYING);
  }

  private void SetExternalDuration(int duration) {
      if(mListener != null) mListener.onKVideoEditPlayerSeekBarSetDuration(duration);
  }

  private void VideoEditPlayerPrepared() {
      if(mListener != null) mListener.onKVideoEditPlayerPrepared();
  }
}